package com.jemframework.utils;

import java.util.Calendar;
import java.util.Date;

public class DateUtils {

  public static long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
  public static long WEEK_IN_MILLIS = 1000 * 60 * 60 * 24 * 7;

  /**
   * Returns <code>true</code> if <code>date</code> represents today.
   * 
   * @param anotherDate
   * @return
   */
  public static boolean isSameDay(Date date1, Date date2) {

    Calendar calendar1 = Calendar.getInstance();
    calendar1.setTime(date1);

    Calendar calendar2 = Calendar.getInstance();
    calendar2.setTime(date2);

    boolean isSameDay = true;

    isSameDay &= calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
    isSameDay &= calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
    isSameDay &= calendar1.get(Calendar.DATE) == calendar2.get(Calendar.DATE);

    return isSameDay;
  }

  /**
   * Normalizes <code>date</code> i.e resets hours, minutes, seconds and
   * milliseconds. The result date is returned as result.
   * 
   * @param date
   * @return
   */
  public static Date normalizeDate(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
  }

  /**
   * Returns <code>true</code> if <code>date</code> is between
   * <code>start</code> and <code>end</code>.
   * 
   * @param date
   * @param start
   * @param end
   * @return
   */
  public static boolean isDateBetween(Date date, Date start, Date end) {

    Date normalizedDate = normalizeDate(date);
    Date normalizedStartDate = normalizeDate(start);
    Date normalizedEndDate = normalizeDate(end);

    boolean isAfterStart = normalizedStartDate.compareTo(normalizedDate) <= 0;
    boolean isBeforeEnd = normalizedDate.compareTo(normalizedEndDate) <= 0;

    return isAfterStart && isBeforeEnd;
  }

  /**
   * 
   * @param date
   * @return
   */
  public static boolean isSameWeek(Date date1, Date date2) {
    return isSameWeek(date1, date2, true);
  }

  /**
   * 
   * @param date
   * @return
   */
  public static boolean isSameWeek(Date date1, Date date2, boolean weekStartsWithMonday) {

    // Ensure that date1 doesn't go after date2.
    if (date1.after(date2)) {
      Date tmp = date1;
      date1 = date2;
      date2 = tmp;
    }

    Date normalizedDate1 = normalizeDate(date1);
    Date normalizedDate2 = normalizeDate(date2);

    long millis1 = normalizedDate1.getTime();
    long millis2 = normalizedDate2.getTime();

    long delta = millis2 - millis1;

    if (delta == 0) {
      // The dates represent same day
      // -> they belong to the same week.
      return true;
    }

    if (delta >= WEEK_IN_MILLIS) {
      // Delta is greater or equal to one week
      // -> the dates don't belong to same week.
      return false;
    }

    Calendar calendar = Calendar.getInstance();

    calendar.setTime(date1);
    int dayOfWeek1 = calendar.get(Calendar.DAY_OF_WEEK);

    calendar.setTime(date2);
    int dayOfWeek2 = calendar.get(Calendar.DAY_OF_WEEK);

    // Note that Calendar.SUNDAY == 1.
    if (weekStartsWithMonday) {
      if (dayOfWeek1 == Calendar.SUNDAY) {
        return dayOfWeek2 >= 7;
      }
      if (dayOfWeek2 == Calendar.SUNDAY) {
        return dayOfWeek1 <= 7;
      }
    }

    if (dayOfWeek1 > dayOfWeek2) {
      // 1. date1 goes before date2.
      // 2. date2 stays no longer than 7 days after date1.
      // 3. BUT date1 stays after date2 if we compare them as date of week.
      // -> the dates don't belong to same week.
      return false;
    }

    return true;
  }

  public static int getDayOfWeek(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
    return dayOfWeek;
  }

  public static Date addDays(Date date, int days) {
    long millis = date.getTime();
    long resultInMillis = millis + days * DAY_IN_MILLIS;
    Date resultDate = new Date(resultInMillis);
    return resultDate;
  }
}
